Developing Applications for iOS using SwiftUI
Total Lecture: 15
current: [1-3]
Getting Started with SwiftUI
behaves like a …
1 | import SwiftUI |
ContentView
结构体的行为类似于View
,函数式编程关注的是功能、行为,而不是数据。面向对象编程的根源(root)是数据封装。函数式编程更像是行为封装。
1 |
|
Int
,String
都是结构体,View
不是一个结构体,是一种可以表现的东西,称为协议。
Computed Property
1 | struct ContentView: View { |
变量的值没有存储在某个地方,是只读变量,是计算出来的。body
用var
,因为这里可能有变量,导致它在每次调用它时返回不同的东西。
some View
意味着这个变量的类型必须是世界上的任何结构,只要它的行为像View
一样
creating instances of structs
1 | Image(systemName: "globe") |
named parameters
systemName
parameter defaults
1 | VStack(alignment: .leading, spcing: 20) { |
1 | VStack(){ |
VStack()
后面大括号里实际上是一个函数。该函数返回一个View
,完整的调用代码如下
1 | import SwiftUI |
VStack
是一个接受这个参数的试图。函数里面返回的是一个打包为TupleView
函数式编程,函数始终作为参数传递给其他事物。
@ViewBuilder
将View
列表转换为一个TupleView
组合视图的东西称为@ViewBuilder
TupleView(bag of Lego)
View modifier
.imageScale()
,.foregroundColor()
,.padding()
,这些是函数,也叫View modifier
,因为它的作用是在View
上调用它,Image
是一个行为类似于View
的结构体,因此可以在其上调用这些函数。
View modifier scope
1 | import SwiftUI |
VStack
中的所有内容都获得了.font(.largeTitle)
,.foregroundColor(.orange)
修饰符。VStack、HStacks、Grid
和其它装有乐高积木的东西,当你在它们上面做一些对整个东西来说没有实际意义的事情时,它们会把它传递给里面的东西。
.imageScale(.small)
被应用于文本,而文本忽略了它,因为它不是图像。因此这提供了很大的灵活性,能够将这些东西放在外部,并让它们只应用于内部真正关心的东西。
More SwiftUI
Progres of Memorize Project
1 | import SwiftUI |
some View
1 | struct ContentView: View { |
Cannot convert return expression of type
VStack<TupleView<Text,Text>>
to return typeText
trailing closure syntax
1 | ZStack(alignment: .center , content:{ |
ZStack
是一个行为类似于View
的结构体,有两个参数。任何创建对象或函数的最后一个参数是函数本身,这里是一个返回视图(TupleView
)的函数,可以省略掉它的标签content
1 | ZStack(alignment:. center) { |
alignment
默认值为center
所以上面可简写成
1 | ZStack() { |
这里()
因为是尾随闭包语法,也可以删除,简写成
1 | ZStack { |
locals in @ViewBilder
1 | ZStack { |
let VS var
1 | let base: RoundedRectangle = RoundedRectangle(cornerRadius: 12) |
视图是只读的,所以无论如何都无法改变它
根据是否需要改变事物来在let和var之间做出决定,请始终从let开始,如果你尝试改变它,编译器就会报错,把let想象成真正定义变量的方式,而var更像是标记你要更改的内容。因为当你阅读某人的代码时,你可以知道他们将要在代码的某个地方改变这个东西,本质上这只是一个常量。
Views are immutable
1 |
|
Cannot assign to property:’self’ is immutable
即使isFaceUp声明为var,可变只是在最开始而已
@State
@State var isFaceUp = false
创建一个指向isFaceUp
的指针,用于保存isFaceUp
,所以指针本身不会改变,它指向的东西可以改变,所以它满足View
不能改变但isFaceUp
可以改变
如果你有想要改变的变量,以便在
View
结构体中使用它,且只适用于临时状态,比如如果你在动画的中间,你想跟踪动画的开始或结束或类似的东西,它不是用来存储游戏状态的。
ViewBuilder can do
ViewBuilder中不能使用for
,可以做三件事,条件、列表、局部变量
ForEach
这里进行for
循环的方式是使用特殊的视图,称为ForEach
,它是a bag of lego
1 | ForEach(0..<4, id: \.self) { index in |
这里有一个ViewBuilder
,它实际上有一个参数index
(arguments to closures)。
因为ViewBuilder
是一个函数,函数可以有参数
ForEach
会跟踪视图与index
的匹配
Button
1 | HStack{ |
label
后面的闭包是一个ViewBuilder
,Button
可以制作成任何极其复杂的lego bag
为什么.font
要应用于图像?
系统图像会尝试追踪它们附近事物的字体,它们尝试以内联方式工作,因此无论你将其设置为何字体,它们都会与之匹配,然后图像比例与之相关。
implicit return
1 | var cards: some View { |
为什么这里可以省略return
,cards
是一个计算属性,它返回HStack{}
,它不是ViewBuilder
,HStack
中的内容是ViewBuilder
,但是HStack
本身不是,这里只是一个普通的函数,它只有一行代码,所以不需要return
,这种称为隐式返回(适用于函数和计算属性)。
internal VS external parameter names
1 | func cardCountAdjuster(by offset: Int, symbol:String) -> some View{ |
有两个标签时by offset
,第一个by
是调用者使用的标签,第二个offset
是在函数中使用,symbol
既是外部名称又是内部名称
LazyVGrid
1 | var cards: some View { |
LazyVGrid
不会将子视图堆叠在一起,而是将它们排列在一个具有一定列数的网格中。
LazyVGrid适用尽可能少的空间,HStack适用尽可能大的空间
Group
一个装有乐高玩具的袋子,称为Group
,有点像ForEach
,它只是一个容纳其它乐高的组,所以你可以对它们应用view modifier
1 | ZStack{ |
aspectRatio
1 | var cards: some View { |
.fit
意思是将它放入可用空间
MVVM
MVVM (Design paradigm)
Separating “Logic and Data” from “UI”
we call this logic and data our Model
UI实际上就像模型所供给的可参数化的外壳
The UI is basically just a “parametrizable” shell that the Model feed and brings to life
Think of the UI as a visual manifestation of the Model
Connecting the Model to the UI
- the model just be an
@State
in aView
- the model only be accessable via a gatekeeper
ViewModel
class
Swift Type System
struct
class
protocol
“don’t care” type(aka generics)
enum
functions
struct and class
struct是函数式编程的基础
class是面向对象编程的基础
面向对象 VS 函数式
面向对象编程: 数据封装,封装现实世界概念或事物的数据,然后将功能与该数据相关联,通过将其全部封装到一个具有与该数据相关的函数的结构中。
它增加了继承,因为有些东西非常像其他东西,但有一些细微的修改,所以我们可以从它们中继承,然后调整它们成为它的特定实例或其他什么。
函数式编程: 行为封装,真正要做的就是描述事物的行为方式,而当谈到数据时,它就在幕后。在函数式编程中,我们真正想要的东西之一是可证明性(计算机科学中的概念),面向对象编程中无法实现,因为你不知道谁将拥有一个指向你的类的指针,而在函数式编程中,你可以,因为你总是传递其中的内容的副本,没人可以从外部干扰该函数
可证明性: 你希望能够获取一段代码并证明它无论发生什么,无论它处于什么环境或其他什么情况下都会做它会做的事情。
为什么要为视图模型使用一个类?
我们的视图模型是共享的,可能在许多不同的视图之间共享,许多视图都想获得模型
Generics
sometimes we just don’t care,type agnostic
1 | struct Array<Element> { |
Usage
1 | var a = Array<Int>() |
Type Parameter
想象一下你有一个不关心的事情,你强迫它行为像(behaves like a)某种东西,这为你构建编程接口提供了很大的灵活性。
protocol
协议看起来像没有实现的类或结构体,它只是一种行为的声明描述,
A protocol is sort of a “stripped-down” struct/class
1 | protocol Moveable { |
PortableThing
now conforms to(aka “behaves like a”)Moveable
1 | struct PortableThing: Moveable { |
what is a protocol used for?
- constrains and gains 既能限制事物,又能获得事物
- turning don’t cares into “somewhat cares”
1 | struct Array<Element> where Element: Equatable |
Functions as Types
functions are types
1 | (Int,Int) -> Bool // takes two Ints and returns a Bool |
var foo: (Double) -> Void
func doSomething(what: () -> Bool)
这些都不包含参数名称,当你指定参数类型时,不包括参数名称。
Usge:
1 | var operation: (Double) -> Double |
Closures
闭包又叫内联函数
1 | ZStack(content:{ |
它的类型是一个不带参数并返回View
的函数